Skip to main content

pacenotes_installer\engine\execute/
restore_point.rs

1/// Creates `.pacenotes\restore\<now>\` holding the ledger as it is before the
2/// run, which is what lets a restore also remove what was installed afterwards.
3pub fn open_restore_point(
4    game_dir: &std::path::Path,
5    ledger: &crate::ledger::Ledger,
6    watcher: &mut dyn crate::engine::Watcher,
7) -> Result<String, super::ExecuteError> {
8    let name = unused_point_name(
9        game_dir,
10        &crate::utc_time::file_stamp(crate::utc_time::unix_now()),
11    );
12    let dir = restore_dir(game_dir, &name);
13    std::fs::create_dir_all(dir.join("files"))
14        .map_err(|e| super::ExecuteError::io(format!("creating the restore point {name}"), e))?;
15    ledger
16        .save_snapshot(&snapshot_path(game_dir, &name))
17        .map_err(|e| super::ExecuteError::io("recording what is installed", e))?;
18    watcher.log(&format!("restore point {name}"));
19    Ok(name)
20}
21
22/// Where restore points live, under the game's state directory.
23pub const RESTORE_DIR: &str = "restore";
24
25pub fn restore_dir(game_dir: &std::path::Path, point: &str) -> std::path::PathBuf {
26    game_dir
27        .join(crate::ledger::STATE_DIR)
28        .join(RESTORE_DIR)
29        .join(point)
30}
31
32/// The ledger as it was before the run that followed `point`.
33pub fn snapshot_path(game_dir: &std::path::Path, point: &str) -> std::path::PathBuf {
34    restore_dir(game_dir, point).join("ledger.json")
35}
36
37/// `stamp`, or `stamp-2`, `stamp-3`… when that point exists, since two runs
38/// can share a second and the second must not overwrite the first's way back;
39/// the suffixed name still sorts after the plain one.
40fn unused_point_name(game_dir: &std::path::Path, stamp: &str) -> String {
41    let free = |name: &str| !restore_dir(game_dir, name).exists();
42    if free(stamp) {
43        return stamp.to_string();
44    }
45    // Bounded: a hundred runs in one second is not a real case.
46    (2..100)
47        .map(|n| format!("{stamp}-{n}"))
48        .find(|candidate| free(candidate))
49        .unwrap_or_else(|| stamp.to_string())
50}
51
52/// Where `point` keeps its copy of the file at `relative`.
53pub fn saved_copy(game_dir: &std::path::Path, point: &str, relative: &str) -> std::path::PathBuf {
54    crate::relative_path::join(&restore_dir(game_dir, point).join("files"), relative)
55}